package com.smartian.scan.filesearch.task.runnable;


import com.smartian.scan.filesearch.bean.TreeNodeFile;
import com.smartian.scan.filesearch.interfaces.CommonSearchPath;
import com.smartian.scan.filesearch.interfaces.TreeAdapter;
import com.smartian.scan.filesearch.task.exectors.TaskExecutors;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TreeSearchRunnable  implements Runnable {

    private final TreeNodeFile treeNodeFile;
    private final TreeAdapter adapter;
    private static final Map<String, Pattern> REGEXP_PATTERN_POOL = new LinkedHashMap<>();


    public TreeSearchRunnable(TreeAdapter adapter, TreeNodeFile  treeNodeFile){
        this.adapter = adapter;
        this.treeNodeFile = treeNodeFile;
    }

    @Override
    public void run() {
        String fullPath = this.treeNodeFile.getFullPath();

        if(this.adapter!=null) {
            this.adapter.onStartSearchTree(fullPath);
        }
        File file = new File(fullPath);

        if(treeNodeFile.getType()==TreeNodeFile.TYPE_ROOT&&file.isFile()){
            if(this.adapter==null){
                this.adapter.onSearchedTreeFiles(Arrays.asList(file));
                this.adapter.onStopSearchTree(fullPath);
            }
            return;
        }

        if(CommonSearchPath.DIR_SDCARD.startsWith(treeNodeFile.getFullPath()) && !CommonSearchPath.DIR_SDCARD.equals(treeNodeFile.getFullPath())){

            dispatchNextTask(treeNodeFile.getChildren());
            if(this.adapter!=null) {
                this.adapter.onStopSearchTree(fullPath);
            }

            return;
        }

        File[] listFiles = file.listFiles();
        if (listFiles == null || listFiles.length == 0) {
            if (this.adapter != null) {
                this.adapter.onStopSearchTree(fullPath);
            }
            return;
        }

        List<TreeNodeFile> children =  this.treeNodeFile.getChildren();
        List<File> scanFiles = new ArrayList<>();

        for (File itemFile : listFiles){

            if(file==null || !file.exists()) {
               continue;
            }

            if(findChildRule(children,itemFile)){

                if(itemFile.isDirectory()) {
                    this.treeNodeFile.addChild(itemFile.getName());
                }else{
                    scanFiles.add(itemFile);
                }
                continue;
            }

            TreeNodeFile regExpTreeNode =  matchNodeFile(children,itemFile);
            if(regExpTreeNode==null){
                continue;
            }
            if(itemFile.isDirectory()) {
                TreeNodeFile childNodeFile = swapChildren(itemFile, regExpTreeNode);
                this.treeNodeFile.addChild(childNodeFile);
            }else{
                scanFiles.add(itemFile);
            }
        }

        children  = this.treeNodeFile.getChildren();
        dispatchNextTask(children);

        if(this.adapter!=null) {
            this.adapter.onSearchedTreeFiles(scanFiles);
            this.adapter.onStopSearchTree(fullPath);
        }

    }

    private TreeNodeFile swapChildren(File itemFile, TreeNodeFile regExpNode) {

        TreeNodeFile  childNodeFile = this.treeNodeFile.getTreeFile(itemFile.getName());
        List<TreeNodeFile> children =  regExpNode.getChildren();
        if(children==null){
            return childNodeFile;
        }

        for (int i=0;i<children.size();i++){
            childNodeFile.addChild(children.get(i));
        }

        return childNodeFile;
    }

    private void dispatchNextTask(List<TreeNodeFile> children) {
        if(children==null || children.size()==0) return ;

        for (TreeNodeFile treeNode : children){
            if(treeNode==null) continue;
            if(treeNode.isFileRegExp()) continue;

            if( this.adapter!=null){
                this.adapter.onDisptachTree(treeNode);
            }
        }
    }



    private boolean findChildRule(List<TreeNodeFile> children, File file) {


        if(children==null){
            //如果child为空，说明全匹配
            return true;
        }
        for (TreeNodeFile child : children){
            if(equals(file.getName(),child.getPathName())){
                return true;
            }

        }
        return false;

    }

    private TreeNodeFile matchNodeFile(List<TreeNodeFile> children, File file) {

        for (TreeNodeFile child : children){
            if(isRegExMatch(file,child)){
                return child;
            }
        }

        return null;

    }

    private boolean isRegExMatch(File file, TreeNodeFile child) {

        if(file.isFile() && child.isFileRegExp() || file.isDirectory()){

            Pattern pattern =  getPattern(child.getPathName());
            Matcher matcher = pattern.matcher(file.getName());
            return matcher!=null && matcher.matches();
        }

        return false;
    }

    private Pattern getPattern(String pathName) {

        Pattern pattern = REGEXP_PATTERN_POOL.get(pathName);
        if(pattern==null){
            pattern = Pattern.compile(pathName);
            REGEXP_PATTERN_POOL.put(pathName,pattern);
        }
        return pattern;
    }


    public static boolean equals(CharSequence a, CharSequence b) {
        if (a == b) return true;
        int length;
        if (a != null && b != null && (length = a.length()) == b.length()) {
            if (a instanceof String && b instanceof String) {
                return a.equals(b);
            } else {
                for (int i = 0; i < length; i++) {
                    if (a.charAt(i) != b.charAt(i)) return false;
                }
                return true;
            }
        }
        return false;
    }
}
